﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

using Telerik.Windows.Controls;
using Telerik.Windows.Controls.GridView;

namespace Rio.Manager.Application.Behaviors
{
    public class RadGridViewRowMouseDoubleClickBehavior : TriggerBase<RadGridView>
    {
        #region Fields

        public static readonly DependencyProperty AllowedPixelMarginProperty = 
           DependencyProperty.Register("AllowedPixelMargin", typeof(int), TriggerType,
           new PropertyMetadata(4, OnAllowedPixelMarginPropertyChanged));
        public static readonly DependencyProperty DelayBetweenClicksProperty = 
           DependencyProperty.Register("DelayBetweenClicks", typeof(TimeSpan), TriggerType,
           new PropertyMetadata(TimeSpan.FromMilliseconds(500), OnDelayBetweenClicksPropertyChanged));
        public static readonly DependencyProperty IgnoreDetailsPresenterProperty = 
           DependencyProperty.Register("IgnoreDetailsPresenter", typeof(bool), TriggerType,
           new PropertyMetadata(false, OnIgnoreDetailsPresenterPropertyPropertyChanged));
        public static readonly DependencyProperty IgnoreDetailsPresenterUnlessSecondClickProperty = 
           DependencyProperty.Register("IgnoreDetailsPresenterUnlessSecondClick", typeof(bool), TriggerType,
           new PropertyMetadata(false, OnIgnoreDetailsPresenterUnlessSecondClickPropertyPropertyChanged));

        private static readonly Type TriggerType = typeof(RadGridViewRowMouseDoubleClickBehavior);

        private Point _firstMouseDownPosition;
        private GridViewRow _firstMouseLeftButtonDownGridViewRow;
        private MouseButtonEventHandler _gridViewMouseLeftButtonDownHandler;
        private bool _hasFirstMouseDownOccured = false;
        private DateTime _lastMouseDownDateTime = DateTime.Now;

        #endregion Fields

        #region Properties

        public int AllowedPixelMargin
        {
            get { return (int)GetValue(AllowedPixelMarginProperty); }
            set { SetValue(AllowedPixelMarginProperty, value); }
        }

        public TimeSpan DelayBetweenClicks
        {
            get { return (TimeSpan)GetValue(DelayBetweenClicksProperty); }
            set { SetValue(DelayBetweenClicksProperty, value); }
        }

        public bool IgnoreDetailsPresenter
        {
            get { return (bool)GetValue(IgnoreDetailsPresenterProperty); }
            set { SetValue(IgnoreDetailsPresenterProperty, value); }
        }

        public bool IgnoreDetailsPresenterUnlessSecondClick
        {
            get { return (bool)GetValue(IgnoreDetailsPresenterUnlessSecondClickProperty); }
            set { SetValue(IgnoreDetailsPresenterUnlessSecondClickProperty, value); }
        }

        #endregion Properties

        #region Methods

        protected virtual void OnAllowedPixelMarginPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        protected override void OnAttached()
        {
            if (AssociatedObject != null)
            {
                _gridViewMouseLeftButtonDownHandler = new MouseButtonEventHandler(OnGridViewMouseLeftButtonDown);
                AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent, _gridViewMouseLeftButtonDownHandler, true);
            }
        }

        protected virtual void OnDelayBetweenClicksPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        protected override void OnDetaching()
        {
            if (AssociatedObject != null && _gridViewMouseLeftButtonDownHandler != null)
            {
                AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent, _gridViewMouseLeftButtonDownHandler);
                _gridViewMouseLeftButtonDownHandler = null;
            }
        }

        protected virtual void OnIgnoreDetailsPresenterPropertyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        protected virtual void OnIgnoreDetailsPresenterUnlessSecondClickPropertyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        private static void OnAllowedPixelMarginPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ( (RadGridViewRowMouseDoubleClickBehavior)sender ).OnAllowedPixelMarginPropertyChanged(e);
        }

        private static void OnDelayBetweenClicksPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ( (RadGridViewRowMouseDoubleClickBehavior)sender ).OnDelayBetweenClicksPropertyChanged(e);
        }

        private static void OnIgnoreDetailsPresenterPropertyPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ( (RadGridViewRowMouseDoubleClickBehavior)sender ).OnIgnoreDetailsPresenterPropertyPropertyChanged(e);
        }

        private static void OnIgnoreDetailsPresenterUnlessSecondClickPropertyPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ( (RadGridViewRowMouseDoubleClickBehavior)sender ).OnIgnoreDetailsPresenterUnlessSecondClickPropertyPropertyChanged(e);
        }

        private bool IsValidGridViewRowLocation(RadGridView radGridView, Point mousePosition, ref object rowDataContext, bool isSecondClick)
        {
            GeneralTransform gt = radGridView.TransformToVisual(App.Current.RootVisual);
            Point mousePositionOffset = gt.Transform(mousePosition);

            IEnumerable<UIElement> hitElements = VisualTreeHelper.FindElementsInHostCoordinates(mousePositionOffset, radGridView);

            if (IgnoreDetailsPresenter || ( isSecondClick == false && IgnoreDetailsPresenterUnlessSecondClick ))
            {
                var gridDetails = hitElements.OfType<DetailsPresenter>().FirstOrDefault();

                if (gridDetails != null)
                {
                    return false;
                }
            }

            var gridViewRow = hitElements.OfType<GridViewRow>().FirstOrDefault();

            if (gridViewRow != null)
            {
                rowDataContext = gridViewRow.DataContext;
            }

            if (isSecondClick)
            {
                return gridViewRow != null && gridViewRow == _firstMouseLeftButtonDownGridViewRow;
            }

            _firstMouseLeftButtonDownGridViewRow = gridViewRow;
            return gridViewRow != null;
        }

        private void OnGridViewMouseLeftButtonDown(object sender, MouseButtonEventArgs args)
        {
            DateTime currentMouseDownDateTime = DateTime.Now;
            TimeSpan timeSpanBetweenButtonDowns = currentMouseDownDateTime - _lastMouseDownDateTime;

            Point currentMousePosition = args.GetPosition(AssociatedObject);
            object rowDataContext = null;

            // First Mouse Down
            if (timeSpanBetweenButtonDowns > DelayBetweenClicks || _hasFirstMouseDownOccured == false)
            {
                if (IsValidGridViewRowLocation(AssociatedObject, currentMousePosition, ref rowDataContext, false))
                {
                    _firstMouseDownPosition = currentMousePosition;
                    _hasFirstMouseDownOccured = true;
                    _lastMouseDownDateTime = DateTime.Now;
                }
            }
            else
            {
                // Second mouse down is
                // A.) Within specified miliseconds of first mouse down.
                // B.) Allowed pixels margin of first mouse down
                // C.) Pointer position is within a GridViewRow
                if (( Math.Abs(_firstMouseDownPosition.X - currentMousePosition.X) < AllowedPixelMargin && Math.Abs(_firstMouseDownPosition.Y - currentMousePosition.Y) < AllowedPixelMargin )
                    && IsValidGridViewRowLocation(AssociatedObject, currentMousePosition, ref rowDataContext, true)
                    )
                {
                    InvokeActions(args);
                }

                _hasFirstMouseDownOccured = false;
            }
        }

        #endregion Methods
    }
}